1 module hip.hiprenderer.backend.metal.mtlvertex;
2 
3 version(AppleOS):
4 import metal;
5 import hip.hiprenderer;
6 import hip.error.handler;
7 import hip.hiprenderer.backend.metal.mtlrenderer;
8 import hip.hiprenderer.backend.metal.mtlshader;
9 
10 
11 MTLResourceOptions mtlOptions(HipBufferUsage usage)
12 {
13     final switch(usage)
14     {
15         case HipBufferUsage.DYNAMIC:
16             return MTLResourceOptions.StorageModeShared;
17         case HipBufferUsage.STATIC:
18             return MTLResourceOptions.StorageModePrivate;
19         case HipBufferUsage.DEFAULT:
20             return MTLResourceOptions.DefaultCache;
21     }
22 }
23 
24 
25 __gshared MTLBuffer boundIndexBuffer;
26 class HipMTLIndexBuffer : IHipIndexBufferImpl
27 {
28     MTLBuffer buffer;
29     MTLResourceOptions options;
30     MTLCommandQueue cmdQueue;
31     MTLDevice device;
32 
33     this(MTLDevice device, MTLCommandQueue cmdQueue, size_t length, HipBufferUsage usage)
34     {
35         this.device = device;
36         this.cmdQueue = cmdQueue;
37         options = usage.mtlOptions;
38         buffer = device.newBuffer(length*index_t.sizeof, options);
39         buffer.retain();
40     }
41     void bind()
42     {
43         boundIndexBuffer = buffer;
44     }
45 
46     void unbind()
47     {
48         if(boundIndexBuffer is buffer) boundIndexBuffer = null;
49     }
50 
51     void setData(const index_t[] data)
52     {
53         if(options == MTLResourceOptions.StorageModePrivate)
54         {
55             MTLBuffer temp = device.newBuffer(data.ptr, data.length*index_t.sizeof, MTLResourceOptions.StorageModeShared);
56             MTLCommandBuffer cmdBuffer = cmdQueue.defaultCommandBuffer();
57             MTLBlitCommandEncoder cmdEncoder = cmdBuffer.blitCommandEncoder;
58             cmdEncoder.copyFromBuffer(temp, 0, buffer, 0, data.length*index_t.sizeof);
59             cmdEncoder.endEncoding();
60             cmdBuffer.commit();
61             cmdBuffer.waitUntilCompleted();
62         }
63         else
64         {
65             if(buffer) buffer.release();
66             buffer = device.newBuffer(data.ptr, data.length*index_t.sizeof, options);
67             buffer.retain();
68         }
69     }
70 
71     void updateData(int offset, const index_t[] data)
72     {
73         buffer.contents[offset..offset+data.length*index_t.sizeof] = cast(void[])(data[]);
74     }
75 }
76 
77 class HipMTLVertexBuffer : IHipVertexBufferImpl
78 {
79     MTLBuffer buffer;
80     MTLCommandQueue cmdQueue;
81     MTLResourceOptions options;
82     MTLDevice device;
83 
84     this(MTLDevice device, MTLCommandQueue cmdQueue, size_t size, HipBufferUsage usage)
85     {
86         this.device = device;
87         this.cmdQueue = cmdQueue;
88         options = usage.mtlOptions;
89         buffer = device.newBuffer(size, options);
90         buffer.retain();
91     }
92     void bind(){}
93     void unbind(){}
94     void setData(const void[] data)
95     {
96         if(options == MTLResourceOptions.StorageModePrivate)
97         {
98             MTLBuffer temp = device.newBuffer(data.ptr, data.length, MTLResourceOptions.StorageModeShared);
99             MTLCommandBuffer cmdBuffer = cmdQueue.defaultCommandBuffer();
100             MTLBlitCommandEncoder cmdEncoder = cmdBuffer.blitCommandEncoder;
101             cmdEncoder.copyFromBuffer(temp, 0, buffer, 0, data.length);
102             cmdEncoder.endEncoding();
103             cmdBuffer.commit();
104             cmdBuffer.waitUntilCompleted();
105             if(cmdBuffer.error)
106                 NSLog("Command Buffer Error: %@".ns, cmdBuffer.error);
107         }
108         else
109         {
110             if(buffer) buffer.release();
111             buffer = device.newBuffer(data.ptr, data.length, options);
112             buffer.retain();
113         }
114     }
115 
116     void updateData(int offset, const void[] data)
117     {
118         buffer.contents[offset..offset+data.length] = data[];
119     }
120 }
121 
122 MTLVertexFormat mtlVertexFormatFromAttributeInfo(HipVertexAttributeInfo i)
123 {
124     final switch(i.valueType)
125     {
126         case HipAttributeType.Rgba32: return MTLVertexFormat.uchar4Normalized;
127         case HipAttributeType.Float:
128             final switch(i.count)
129             {
130                 case 1: return MTLVertexFormat.float1;
131                 case 2: return MTLVertexFormat.float2;
132                 case 3: return MTLVertexFormat.float3;
133                 case 4: return MTLVertexFormat.float4;
134             }
135         case HipAttributeType.Int:
136             final switch(i.count)
137             {
138                 case 1: return MTLVertexFormat.int1;
139                 case 2: return MTLVertexFormat.int2;
140                 case 3: return MTLVertexFormat.int3;
141                 case 4: return MTLVertexFormat.int4;
142             }
143         case HipAttributeType.Uint:
144             final switch(i.count)
145             {
146                 case 1: return MTLVertexFormat.uint1;
147                 case 2: return MTLVertexFormat.uint2;
148                 case 3: return MTLVertexFormat.uint3;
149                 case 4: return MTLVertexFormat.uint4;
150             }
151         case HipAttributeType.Bool:
152             final switch(i.count)
153             {
154                 case 1: return MTLVertexFormat.char1;
155                 case 2: return MTLVertexFormat.char2;
156                 case 3: return MTLVertexFormat.char3;
157                 case 4: return MTLVertexFormat.char4;
158             }
159     }
160     assert(false, "Unknown format");
161 }
162 
163 class HipMTLVertexArray : IHipVertexArrayImpl
164 {
165     MTLVertexDescriptor descriptor;
166     HipMTLVertexBuffer vBuffer;
167     HipMTLIndexBuffer iBuffer;
168 
169     HipMTLRenderer mtlRenderer;
170     MTLDevice device;
171 
172     this(MTLDevice device, HipMTLRenderer mtlRenderer)
173     {
174         this.device = device;
175         this.mtlRenderer = mtlRenderer;
176         descriptor = MTLVertexDescriptor.vertexDescriptor();
177         descriptor.layouts[1].stepFunction = MTLVertexStepFunction.PerVertex;
178         descriptor.layouts[1].stepRate = 1;
179     }
180 
181     void bind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo)
182     {
183         if(vbo is null || ebo is null)
184             return;
185         vBuffer = cast(HipMTLVertexBuffer)vbo;
186         iBuffer = cast(HipMTLIndexBuffer)ebo;
187         vbo.bind();
188         ebo.bind();
189         mtlRenderer.getEncoder.setVertexBuffer(vBuffer.buffer, 0, 1);
190     }
191 
192     void unbind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo)
193     {
194         mtlRenderer.getEncoder.setVertexBuffer(null, 0, 1);
195         vbo.unbind();
196         ebo.unbind();
197         vBuffer = null;
198         iBuffer = null;
199     }
200 
201     /** 
202      * HipMTLRenderer will ALWAYS assume that:
203      *  Buffer 0 is for uniforms
204      *  Buffer 1 is for vertex attributes
205      */
206     void setAttributeInfo(ref HipVertexAttributeInfo info, uint stride)
207     {
208         descriptor.layouts[1].stride = stride;
209         MTLVertexAttributeDescriptor attribute = descriptor.attributes[info.index];
210         attribute.format = mtlVertexFormatFromAttributeInfo(info);
211         attribute.offset = info.offset;
212         attribute.bufferIndex = 1;
213     }
214 
215     void createInputLayout(Shader s)
216     {
217         HipMTLShaderProgram shader = (cast(HipMTLShaderProgram)s.shaderProgram);
218         shader.createInputLayout(device, descriptor);
219     }
220 }